home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / xconq / unit.c < prev    next >
C/C++ Source or Header  |  1995-05-09  |  20KB  |  716 lines

  1. /* Copyright (c) 1987, 1988  Stanley T. Shebs, University of Utah. */
  2. /* This program may be used, copied, modified, and redistributed freely */
  3. /* for noncommercial purposes, so long as this notice remains intact. */
  4.  
  5. #pragma comment(exestr, "@(#) unit.c 12.1 95/05/09 ")
  6.  
  7. /* RCS $Header: unit.c,v 1.5 88/07/20 15:23:42 shebs Exp $ */
  8.  
  9. /* This file contains all code relating specifically to units. */
  10.  
  11. /* Since units appear and disappear with distressing regularity (so it seems */
  12. /* to the player trying to keep up with all of them!), we have a little */
  13. /* storage manager for them.  Since this sort of thing is tricky, there is a */
  14. /* two-level procedure for getting rid of units.  First the hit points are */
  15. /* reduced to zero, at which point the unit is considered "dead".  At the */
  16. /* end of a turn, we actually GC the dead ones.  At this point their type */
  17. /* slot becomes NOTHING, and they are available for allocation. */
  18.  
  19. #include "config.h"
  20. #include "misc.h"
  21. #include "period.h"
  22. #include "side.h"
  23. #include "unit.h"
  24. #include "map.h"
  25. #include "global.h"
  26.  
  27. char *ordinal();
  28.  
  29. Unit *units;           /* array of all units */
  30. Unit *unitlist;        /* pointer to head of list of units */
  31. Unit *tmpunit;         /* global temporary used in several places */
  32.  
  33. char unitbuf[BUFSIZE];
  34.  
  35. int numunits;          /* total number of units in existence */
  36. int maxunits;          /* current size of array of units */
  37. int nextid;            /* next number to be used for ids */
  38. int occdeath[MAXUTYPES];  /* buffer for remembering occupant death */
  39.  
  40. /* Init all the unit entries so we can find them when allocating. */
  41.  
  42. init_units()
  43. {
  44.     int i;
  45.  
  46.     maxunits = INITMAXUNITS;
  47.     units = (Unit *) malloc(maxunits * sizeof(Unit));
  48.     for (i = 0; i < maxunits; ++i) units[i].type = NOTHING;
  49.     unitlist = NULL;
  50.     numunits = 0;
  51.     nextid = 0;
  52. }
  53.  
  54. /* Create a new unit of given type, with given name.  Default all the other */
  55. /* slots - routines that need to will fill them in properly.  This routine */
  56. /* will return a valid unit or NULL.  Note that unit morale starts out at */
  57. /* max, signifying hopeful but inexperienced recruits marching off to war. */
  58.  
  59. Unit *
  60. create_unit(type, name)
  61. int type;
  62. char *name;
  63. {
  64.     int i, r;
  65.     Unit *newunit;
  66.  
  67.     for (i = 0; i < maxunits; ++i) {
  68.     if (units[i].type == NOTHING) break;
  69.     }
  70.     if (i == maxunits) {
  71.     if (grow_unit_array()) {
  72.         return create_unit(type, name);
  73.     } else {
  74.         return NULL;
  75.     }
  76.     }
  77.     newunit = &units[i];
  78.     newunit->type = type;
  79.     if (name == NULL || strlen(name) == 0) {
  80.     newunit->name = NULL;
  81.     } else {
  82.     newunit->name = copy_string(name);
  83.     }
  84.     newunit->x = newunit->y = -1;
  85.     newunit->number = 0;
  86.     newunit->side = NULL;
  87.     newunit->id = nextid++;
  88.     newunit->trueside = NULL;
  89.     newunit->hp = utypes[type].hp;
  90.     newunit->quality = 0;
  91.     newunit->morale = utypes[type].maxmorale;
  92.     newunit->fatigue = 0;
  93.     newunit->product = NOTHING;
  94.     newunit->schedule = 0;
  95.     newunit->built = 0;
  96.     for_all_resource_types(r) newunit->supply[r] = 0;
  97.     newunit->transport = NULL;
  98.     newunit->group = 0;
  99.     newunit->goal = 0;
  100.     newunit->awake = FALSE;   /* i.e., is usually not *temporarily* awake */
  101.     newunit->standing = NULL;
  102.     newunit->occupant = NULL;
  103.     newunit->nexthere = NULL;
  104.     link_in_unit(newunit);
  105.     wake_unit(newunit, FALSE);
  106.     ++numunits;
  107.     return newunit;
  108. }
  109.  
  110. /* Add unit to front of list.  It will be sorted into place shortly. */
  111.  
  112. link_in_unit(unit)
  113. Unit *unit;
  114. {
  115.     unit->next = unitlist;
  116.     unitlist = unit;
  117. }
  118.  
  119. /* Attempt to the number of units that can be played.  We do the obvious - */
  120. /* malloc a new array and copy everything over.  First copy byte-by-byte, */
  121. /* then adjust non-NULL pointers to other units.  Must also take care of */
  122. /* any globals or other variables.  (such as in side structs) */
  123.  
  124. /* At the moment, this seems to cause weird changes in unit behavior which */
  125. /* I don't understand.  One missing thing here is to change all the ptrs */
  126. /* on the world map itself... Another solution would be to do in between */
  127. /* turns, perhaps after flushing dead units. */
  128.  
  129. grow_unit_array()
  130. {
  131. #ifdef GROWABLE
  132.     char *base, *newbase;
  133.     int newmax, i;
  134.     Unit *newunits;
  135.  
  136.     newmax = maxunits + maxunits / 2;
  137.     if (Debug) printf("Extending unit array to hold %d units.\n", newmax);
  138.     newunits = (Unit *) malloc(newmax * sizeof(Unit));
  139.     base = (char *) units;
  140.     newbase = (char *) newunits;
  141.     for (i = 0; i < maxunits * sizeof(Unit); ++i) newbase[i] = base[i];
  142.     for (i = 0; i < maxunits; ++i) {
  143.     if (newunits[i].next) 
  144.         newunits[i].next =
  145.         (Unit *) ((char *) units[i].next + (newbase - base));
  146.     if (newunits[i].nexthere) 
  147.         newunits[i].nexthere =
  148.         (Unit *) ((char *) units[i].nexthere + (newbase - base));
  149.     if (newunits[i].transport) 
  150.         newunits[i].transport =
  151.         (Unit *) ((char *) units[i].transport + (newbase - base));
  152.     if (newunits[i].occupant) 
  153.         newunits[i].occupant =
  154.         (Unit *) ((char *) units[i].occupant + (newbase - base));
  155.     }
  156.     /* Need to patch the map as well, maybe other things? */
  157.     for (i = maxunits; i < newmax; ++i) newunits[i].type = NOTHING;
  158.     free(units);
  159.     maxunits = newmax;
  160.     units = newunits;
  161.     if (unitlist) unitlist = (Unit *) ((char *) unitlist + (newbase - base));
  162.     notify_all("The unit array has been grown.");
  163.     return TRUE;
  164. #else
  165.     notify_all("Can't make any more units!");
  166.     return FALSE;
  167. #endif
  168. }
  169.  
  170. /* Find a good initial unit for the given side.  Only requirement these */
  171. /* days is that it be of the type specified in the period file, and not */
  172. /* already used by somebody.  If enough failed tries to find one, */
  173. /* something may be wrong, but not necessarily. */
  174.  
  175. Unit *
  176. random_start_unit()
  177. {
  178.     int tries = numunits;
  179.     Unit *unit;
  180.  
  181.     while (tries-- > 0) {
  182.     unit = &units[random(numunits)];
  183.     if (neutral(unit) && unit->type == period.firstutype) return unit;
  184.     }
  185.     for_all_units(unit) {
  186.     if (neutral(unit) && unit->type == period.firstutype) return unit;
  187.     }
  188.     return NULL;
  189. }
  190.  
  191. /* A unit occupies a hex either by entering a unit on that hex or by having */
  192. /* the occupant pointer filled in.  If something goes wrong, return false. */
  193. /* This is heavily used. */
  194.  
  195. occupy_hex(unit, x, y)
  196. Unit *unit;
  197. int x, y;
  198. {
  199.     register int u = unit->type, o;
  200.     register Unit *other = unit_at(x, y);
  201.  
  202.     if (other) {
  203.     o = other->type;
  204.     if (could_carry(o, u)) {
  205.         occupy_unit(unit, other);
  206.     } else if (could_carry(u, o)) {
  207.         leave_hex(other);
  208.         occupy_hex(unit, x, y);
  209.         occupy_hex(other, x, y);
  210.     } else {
  211.         return FALSE;
  212.     }
  213.     } else {
  214.     set_unit_at(x, y, unit);
  215.     occupy_hex_aux(unit, x, y);
  216.     all_see_occupy(unit, x, y);
  217.     all_see_hex(x, y);
  218.     }
  219.     return TRUE;
  220. }
  221.  
  222. /* Recursive helper to update everybody's position.  This should be one of */
  223. /* two routine that modify unit positions (leaving is the other). */
  224. /* *Every* occupant will increment viewing coverage - strains realism, */
  225. /* but prevents strange bugs. */
  226.  
  227. occupy_hex_aux(unit, x, y)
  228. Unit *unit;
  229. int x, y;
  230. {
  231.     register Unit *occ;
  232.  
  233.     unit->x = x;  unit->y = y;
  234.     cover_area(unit, x, y, 1);
  235.     for_all_occupants(unit, occ) occupy_hex_aux(occ, x, y);
  236. }
  237.  
  238. /* Decide whether transport has the capability to house the given unit. */
  239. /* Check both basic capacity and relative volumes. */
  240.  
  241. can_carry(transport, unit)
  242. Unit *transport, *unit;
  243. {
  244.     int u = unit->type, u2 = transport->type, total = 0, volume = 0;
  245.     int hold = utypes[transport->type].holdvolume;
  246.     Unit *occ;
  247.     
  248.     if (transport == unit)
  249.     return FALSE;
  250.     for_all_occupants(transport, occ) {
  251.     if (occ->type == u) total++;
  252.     volume += utypes[occ->type].volume;
  253.     }
  254.     if (cripple(transport)) {
  255.     hold = (hold * transport->hp) / (utypes[u2].crippled + 1);
  256.     }
  257.     return ((total + 1 <= utypes[u2].capacity[u]) &&
  258.         (volume + utypes[u].volume <= hold));
  259. }
  260.  
  261. /* Units become passengers by linking into front of transport's passenger */
  262. /* list.  They only wake up if the transport is a moving type, but always */
  263. /* pick up standing orders if any defined.  A passenger will get sorted to */
  264. /* move after transport, at end of turn. */
  265.  
  266. occupy_unit(unit, transport)
  267. Unit *unit, *transport;
  268. {
  269.     Order *newords;
  270.  
  271.     unit->nexthere = transport->occupant;
  272.     transport->occupant = unit;
  273.     unit->transport = transport;
  274.     if (mobile(transport->type)) wake_unit(unit, FALSE);
  275.     if (transport->standing != NULL) {
  276.     newords = (transport->standing->orders)[unit->type];
  277.     if (newords && newords->type != NONE) {
  278.         if (Debug) printf("%s getting orders %s",
  279.                   unit_desig(unit), order_desig(newords));
  280.         copy_orders(&(unit->orders), newords);
  281.     }
  282.     }
  283.     occupy_hex_aux(unit, transport->x, transport->y);
  284.     all_see_occupy(unit, transport->x, transport->y);
  285. }
  286.  
  287. /* Unit departs from a hex by zeroing out pointer if in hex or by being */
  288. /* removed from the list of transport occupants. */
  289. /* Dead units (hp = 0) may be run through here, so don't error out. */
  290.  
  291. leave_hex(unit)
  292. Unit *unit;
  293. {
  294.     register int ux = unit->x, uy = unit->y;
  295.  
  296.     if (ux < 0 || uy < 0) {
  297.     /* Sometimes called twice */
  298.     } else if (unit->transport != NULL) {
  299.     leave_unit(unit, unit->transport);
  300.     leave_hex_aux(unit);
  301.     all_see_leave(unit, ux, uy);
  302.     } else {
  303.     set_unit_at(ux, uy, NULL);
  304.     if (see_exact(unit->side, ux, uy))
  305.         draw_hex(unit->side, ux, uy);
  306. /*    see_hex(unit->side, ux, uy);*/
  307.     leave_hex_aux(unit);
  308.     all_see_leave(unit, ux, uy);
  309.     all_see_hex(ux, uy);
  310.     }
  311. }
  312.  
  313. /* Trash old coordinates (recursively) just in case.  Catches many bugs... */
  314.  
  315. leave_hex_aux(unit)
  316. Unit *unit;
  317. {
  318.     register Unit *occ;
  319.  
  320.     cover_area(unit, unit->x, unit->y, -1);
  321.     unit->x = -1;  unit->y = -1;
  322.     for_all_occupants(unit, occ) leave_hex_aux(occ);
  323. }
  324.  
  325. /* Disembarking unlinks from the list of passengers. */
  326.  
  327. leave_unit(unit, transport)
  328. Unit *unit, *transport;
  329. {
  330.     Unit *occ;
  331.  
  332.     if (unit == transport->occupant) {
  333.     transport->occupant = unit->nexthere;
  334.     } else {
  335.     for_all_occupants(transport, occ) {
  336.         if (unit == occ->nexthere) {
  337.         occ->nexthere = occ->nexthere->nexthere;
  338.         break;
  339.         }
  340.     }
  341.     }
  342.     unit->transport = NULL;
  343. }
  344.  
  345. /* Handle the general situation of a unit changing allegiance from one side */
  346. /* to another.  This is a common internal routine, so no messages here. */
  347.  
  348. unit_changes_side(unit, newside, reason1, reason2)
  349. Unit *unit;
  350. Side *newside;
  351. int reason1, reason2;
  352. {
  353.     Side *oldside = unit->side;
  354.     Unit *occ;
  355.  
  356.     if (oldside != NULL) {
  357.     oldside->units[unit->type]--;
  358.     if (producing(unit)) oldside->building[unit->product]--;
  359.     if (reason2 >= 0) oldside->balance[unit->type][reason2]++;
  360.     update_state(oldside, unit->type);
  361.     }
  362.     if (newside == NULL && !utypes[unit->type].isneutral) {
  363.     kill_unit(unit, -1);
  364.     } else {
  365.     if (newside != NULL) {
  366.         newside->units[unit->type]++;
  367.         if (producing(unit)) newside->building[unit->product]++;
  368.         if (reason1 >= 0) newside->balance[unit->type][reason1]++;
  369.         update_state(newside, unit->type);
  370.     }
  371.     for_all_occupants(unit, occ) {
  372.         unit_changes_side(occ, newside, reason1, reason2);
  373.     }
  374.     }
  375.     if (alive(unit)) {
  376.     cover_area(unit, unit->x, unit->y, -1);
  377.     assign_unit_to_side(unit, newside);
  378.     cover_area(unit, unit->x, unit->y, 1);
  379.     }
  380. }
  381.  
  382. /* Change the product of a unit.  Displays will need appropriate mods. */
  383.  
  384. set_product(unit, type)
  385. Unit *unit;
  386. int type;
  387. {
  388.     if (!neutral(unit) && global.setproduct && type != unit->product) {
  389.     if (unit->product != NOTHING) {
  390.         unit->side->building[unit->product]--;
  391.         update_state(unit->side, unit->product);
  392.     }
  393.     if (type != NOTHING) {
  394.         unit->side->building[type]++;
  395.         update_state(unit->side, type);
  396.     }
  397.     unit->product = type;
  398.     unit->built = 0;
  399.     unit->movesleft--;
  400.     }
  401. }
  402.  
  403. /* Set product completion time.  Startup development cost incurred only */
  404. /* if directed. */
  405. /* Technology development cost only incurred for very first unit that the */
  406. /* side constructs.  Both tech and startup are percent addons. */
  407.  
  408. set_schedule(unit)
  409. Unit *unit;
  410. {
  411.     if (producing(unit)) unit->schedule = build_time(unit, unit->product);
  412. }
  413.  
  414. /* Basic routine to compute how long a unit will take to build something. */
  415.  
  416. build_time(unit, prod)
  417. Unit *unit;
  418. int prod;
  419. {
  420.     int schedule = utypes[unit->type].make[prod];
  421.  
  422.     if (unit->built == 0)
  423.     schedule += ((schedule * utypes[prod].startup) / 100);
  424.     if (unit->side->counts[prod] <= 1)
  425.     schedule += ((schedule * utypes[prod].research) / 100);
  426.     return schedule;
  427. }
  428.  
  429. /* Remove a unit from play.  This is different from making it available for */
  430. /* reallocation - only the unit flusher can do that.  We remove all the */
  431. /* passengers too, recursively.  Sometimes units are "killed twice", so */
  432. /* be sure not to run all this twice.  Also count up occupant deaths, being */
  433. /* sure not to count the unit itself as an occupant. */
  434.  
  435. kill_unit(unit, reason)
  436. Unit *unit;
  437. int reason;
  438. {
  439.     int u = unit->type, u2;
  440.  
  441.     if (alive(unit)) {
  442.     for_all_unit_types(u2) occdeath[u2] = 0;
  443.     leave_hex(unit);
  444.     kill_unit_aux(unit, reason);
  445.     occdeath[u]--;
  446.     }
  447. }
  448.  
  449. /* Trash it now - occupant doesn't need to leave_hex.  Also record a reason */
  450. /* for statistics, and update the apropriate display.  The unit here should */
  451. /* be known to be alive. */
  452.  
  453. kill_unit_aux(unit, reason)
  454. Unit *unit;
  455. int reason;
  456. {
  457.     int u = unit->type;
  458.     Unit *occ;
  459.     
  460.     unit->hp = 0;
  461.     occdeath[u]++;
  462.     if (unit->side != NULL && reason >= 0) {
  463.     unit->side->balance[u][reason]++;
  464.     unit->side->units[u]--;
  465.     if (producing(unit)) unit->side->building[unit->product]--;
  466.     update_state(unit->side, u);
  467.     }
  468.     for_all_occupants(unit, occ) if (alive(occ)) kill_unit_aux(occ, reason);
  469. }
  470.  
  471. /* Get rid of all dead units at once.  It's important to get rid of all */
  472. /* of the dead units, so they don't come back and haunt us. */
  473. /* (This routine is basically a garbage collector, and should not be called */
  474. /* during a unit list traversal.) The process starts by finding the first */
  475. /* live unit, making it the head, then linking around all in the middle. */
  476.  
  477. flush_dead_units()
  478. {
  479.     Unit *unit, *unitprev;
  480.  
  481.     while (unitlist != NULL && !alive(unitlist)) {
  482.     unit = unitlist->next;
  483.     flush_one_unit(unitlist);
  484.     unitlist = unit;
  485.     }
  486.     for_all_units(unitprev) {
  487.     unit = unitprev->next;
  488.     if (unit != NULL && !alive(unit)) {
  489.         unitprev->next = unit->next;
  490.         flush_one_unit(unit);
  491.     }
  492.     }
  493. }
  494.  
  495. /* Keep it clean - hit all links to other places.  Some might not be */
  496. /* strictly necessary, but this is not an area to take chances with. */
  497.  
  498. flush_one_unit(unit)
  499. Unit *unit;
  500. {
  501.     unit->type = NOTHING;
  502.     unit->occupant = NULL;
  503.     unit->transport = NULL;
  504.     unit->nexthere = NULL;
  505.     unit->next = NULL;
  506.     --numunits;
  507. }
  508.  
  509. /* Do multiple passes of bubble sort.  This is intended to improve locality */
  510. /* among unit positions and reduce the amount of scrolling around. */
  511. /* Data is generally coherent, so bubble sort not too bad if we allow */
  512. /* early termination when everything in order. */
  513. /* If slowness objectionable, replace with something clever. */
  514.  
  515. sort_units(doall)
  516. bool doall;
  517. {
  518.     bool flips = TRUE;
  519.     int passes = 0;
  520.     register Unit *prevunit, *unit, *nextunit;
  521.  
  522.     while (flips && (doall || passes < numunits / 10)) {
  523.     flips = FALSE;
  524.     prevunit = NULL;
  525.     unit = unitlist;
  526.     while (unit != NULL) {
  527.         if (unit->next != NULL && !in_order(unit, unit->next)) {
  528.         flips = TRUE;
  529.         nextunit = unit->next;
  530.         if (prevunit != NULL) prevunit->next = nextunit;
  531.         unit->next = nextunit->next;
  532.         nextunit->next = unit;
  533.         prevunit = nextunit;
  534.         if (unit == unitlist) unitlist = nextunit;
  535.         } else {
  536.         prevunit = unit;
  537.         unit = unit->next;
  538.         }
  539.     }
  540.     passes++;
  541.     }
  542.     if (Debug) printf("Sorting passes = %d\n", passes);
  543. }
  544.  
  545. /* This can be pretty lax, with the exception that passengers *must* come */
  546. /* after their transports, or game saving will screw up. */
  547.  
  548. in_order(unit1, unit2)
  549. Unit *unit1, *unit2;
  550. {
  551.     int d1, d2;
  552.  
  553.     if (side_number(unit1->side) < side_number(unit2->side)) return TRUE;
  554.     if (side_number(unit1->side) > side_number(unit2->side)) return FALSE;
  555.     if (!neutral(unit1)) {
  556.     d1 = distance(unit1->side->cx, unit1->side->cy, unit1->x, unit1->y);
  557.     d2 = distance(unit2->side->cx, unit2->side->cy, unit2->x, unit2->y);
  558.     if (d1 < d2) return TRUE;
  559.     if (d1 > d2) return FALSE;
  560.     }
  561.     if (unit1->y > unit2->y) return TRUE;
  562.     if (unit1->y < unit2->y) return FALSE;
  563.     if (unit1->x < unit2->x) return TRUE;
  564.     if (unit1->x > unit2->x) return FALSE;
  565.     if (unit1->transport == NULL && unit2->transport != NULL) return TRUE;
  566.     if (unit1->transport != NULL && unit2->transport == NULL) return FALSE;
  567.     if (unit1 == unit2->transport) return TRUE;
  568.     if (unit1->transport == unit2) return FALSE;
  569.     return TRUE;
  570. }
  571.  
  572. /* A unit runs low on supplies at the halfway point, but only worries about */
  573. /* the essential items.  Formula is the same no matter how/if occupants eat */
  574. /* transports' supplies. */
  575.  
  576. low_supplies(unit)
  577. Unit *unit;
  578. {
  579.     int u = unit->type, r;
  580.  
  581.     for_all_resource_types(r) {
  582.     if ((utypes[u].consume[r] > 0) || (utypes[u].tomove[r] > 0)) {
  583.         if (2 * unit->supply[r] <= utypes[u].storage[r]) return TRUE;
  584.     }
  585.     }
  586.     return FALSE;
  587. }
  588.  
  589. /* Display the standing orders of the given unit. */
  590.  
  591. show_standing_orders(side, unit)
  592. Side *side;
  593. Unit *unit;
  594. {
  595.     int u;
  596.     Order *ords;
  597.  
  598.     if (unit->standing != NULL) {
  599.     sprintf(spbuf, "Orders:  ");
  600.     for_all_unit_types(u) {
  601.         ords = unit->standing->orders[u];
  602.         if (ords && ords->type != NONE) {
  603.         sprintf(tmpbuf, "%s to %s;  ",
  604.             utypes[u].name, order_desig(ords));
  605.         strcat(spbuf, tmpbuf);
  606.         }
  607.     }
  608.     notify(side, "%s", spbuf);
  609.     } else {
  610.     notify(side, "No standing orders defined yet.");
  611.     }
  612. }
  613.  
  614. /* Build a short phrase describing a given unit to a given side. */
  615. /* First we supply identification of side, then of unit itself. */
  616.  
  617. char *
  618. unit_handle(side, unit)
  619. Side *side;
  620. Unit *unit;
  621. {
  622.     char *utypename;
  623.  
  624.     if (unit == NULL || !alive(unit)) return "???";
  625.     utypename = utypes[unit->type].name;
  626.     if (utypes[unit->type].named == 2 && unit->name) return unit->name;
  627.     if (unit->side == NULL) {
  628.     sprintf(unitbuf, "the neutral ");
  629.     } else if (unit->side == side) {
  630.     sprintf(unitbuf, "your ");
  631.     } else {
  632.     sprintf(unitbuf, "the %s ", unit->side->name);
  633.     }
  634.     if (unit->name != NULL) {
  635.     sprintf(tmpbuf, "%s %s", utypename, unit->name);
  636.     } else if (unit->number > 0) {
  637.     sprintf(tmpbuf, "%s %s", ordinal(unit->number), utypename);
  638.     } else {
  639.     sprintf(tmpbuf, "%s", utypename);
  640.     }
  641.     strcat(unitbuf, tmpbuf);
  642.     return unitbuf;
  643. }
  644.  
  645. /* Shorter unit description omits side name, but uses same buffer. */
  646.  
  647. char *
  648. short_unit_handle(unit)
  649. Unit *unit;
  650. {
  651.     if (unit->name == NULL) {
  652.     sprintf(unitbuf, "%s %s",
  653.         ordinal(unit->number), utypes[unit->type].name);
  654.     } else {
  655.     sprintf(unitbuf, "%s", unit->name);
  656.     }
  657.     return unitbuf;
  658. }
  659.  
  660. /* General-purpose routine to take an array of anonymous unit types and */
  661. /* summarize what's in it, using numbers and unit chars. */
  662.  
  663. char *
  664. summarize_units(buf, ucnts)
  665. char *buf;
  666. int *ucnts;
  667. {
  668.     char tmp[BUFSIZE];
  669.     int u;
  670.  
  671.     sprintf(buf, "");
  672.     for_all_unit_types(u) {
  673.     if (ucnts[u] > 0) {
  674.         sprintf(tmp, " %d %c", ucnts[u], utypes[u].uchar);
  675.         strcat(buf, tmp);
  676.     }
  677.     }
  678.     return buf;
  679. }
  680.  
  681. /* Search for a unit with the given id number. */
  682.  
  683. Unit *
  684. find_unit(n)
  685. int n;
  686. {
  687.     Unit *unit;
  688.  
  689.     for_all_units(unit)    if (alive(unit) && unit->id == n) return unit;
  690.     return NULL;
  691. }
  692.  
  693. /* Given a unit character, find the type. */
  694.  
  695. find_unit_char(ch)
  696. char ch;
  697. {
  698.     int u;
  699.  
  700.     for_all_unit_types(u) if (utypes[u].uchar == ch) return u;
  701.     return NOTHING;
  702. }
  703.  
  704. /* Generate a name for a unit, using best acrynomese.  This is invoked when */
  705. /* a new named unit has been created. */
  706.  
  707. char *
  708. make_unit_name(unit)
  709. Unit *unit;
  710. {
  711.     sprintf(spbuf, "%c%c-%c-%02d",
  712.         uppercase(unit->side->name[0]), uppercase(unit->side->name[1]),
  713.         utypes[unit->type].uchar, unit->number);
  714.     return copy_string(spbuf);
  715. }
  716.